#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
专业验证码识别服务集成
集成多个市面上最准确的验证码识别API
"""

import requests
import base64
import time
import json
import sys
import os
import tempfile
import shutil
import importlib
from PIL import Image
import io

# 导入调试日志器（正式版本简化）
try:
    from debug_logger import debug_logger, log_info, log_error, log_exception, log_debug
    DEBUG_ENABLED = False  # 正式版本关闭调试
except ImportError:
    DEBUG_ENABLED = False

# 简化的日志函数（正式版本）
def log_info(msg, **kwargs): pass
def log_error(msg, **kwargs): pass
def log_exception(msg, **kwargs): pass
def log_debug(msg, **kwargs): pass

class ProfessionalCaptchaSolver:
    def __init__(self):
        # 从配置文件加载API密钥
        self.load_config()

    def load_config(self):
        """加载验证码识别配置"""
        import json
        import os

        # 默认配置
        self.twocaptcha_api_key = "YOUR_2CAPTCHA_API_KEY"
        self.anticaptcha_api_key = "YOUR_ANTICAPTCHA_API_KEY"
        self.capmonster_api_key = "YOUR_CAPMONSTER_API_KEY"
        self.chaojiying_user = "YOUR_CHAOJIYING_USER"
        self.chaojiying_pass = "YOUR_CHAOJIYING_PASS"
        self.chaojiying_soft_id = "96001"

        # 尝试从配置文件加载
        config_file = "captcha_config.json"
        if os.path.exists(config_file):
            try:
                with open(config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)

                if "twocaptcha_api_key" in config:
                    self.twocaptcha_api_key = config["twocaptcha_api_key"]
                    print("✅ 已加载2captcha配置")

                if "anticaptcha_api_key" in config:
                    self.anticaptcha_api_key = config["anticaptcha_api_key"]
                    print("✅ 已加载Anti-Captcha配置")

                if "chaojiying_user" in config:
                    self.chaojiying_user = config["chaojiying_user"]
                    self.chaojiying_pass = config.get("chaojiying_pass", "")
                    self.chaojiying_soft_id = config.get("chaojiying_soft_id", "96001")
                    print("✅ 已加载超级鹰配置")

            except Exception as e:
                print(f"⚠️ 加载验证码配置失败: {e}")
        else:
            print("💡 未找到验证码配置文件，将尝试使用ddddocr")
    
    def solve_with_2captcha(self, image_data):
        """使用2captcha识别验证码"""
        try:
            # 将图片转换为base64
            image_base64 = base64.b64encode(image_data).decode('utf-8')
            
            # 提交验证码
            submit_url = "http://2captcha.com/in.php"
            submit_data = {
                'key': self.twocaptcha_api_key,
                'method': 'base64',
                'body': image_base64,
                'phrase': 0,  # 0 = 一个单词, 1 = 多个单词
                'regsense': 1,  # 1 = 区分大小写
                'numeric': 2,  # 2 = 字母和数字
                'min_len': 4,
                'max_len': 4,
                'language': 2,  # 2 = 英文
            }
            
            response = requests.post(submit_url, data=submit_data, timeout=30)
            if response.text.startswith('OK|'):
                captcha_id = response.text.split('|')[1]
                
                # 等待识别结果
                result_url = "http://2captcha.com/res.php"
                for _ in range(30):  # 最多等待30次
                    time.sleep(2)
                    result_response = requests.get(result_url, params={
                        'key': self.twocaptcha_api_key,
                        'action': 'get',
                        'id': captcha_id
                    }, timeout=30)
                    
                    if result_response.text.startswith('OK|'):
                        result = result_response.text.split('|')[1]
                        print(f"2captcha识别结果: {result}")
                        return result
                    elif result_response.text != 'CAPCHA_NOT_READY':
                        break
            
            return None
        except Exception as e:
            print(f"2captcha识别失败: {e}")
            return None
    
    def solve_with_anticaptcha(self, image_data):
        """使用Anti-Captcha识别验证码"""
        try:
            # 将图片转换为base64
            image_base64 = base64.b64encode(image_data).decode('utf-8')
            
            # 创建任务
            create_task_url = "https://api.anti-captcha.com/createTask"
            task_data = {
                "clientKey": self.anticaptcha_api_key,
                "task": {
                    "type": "ImageToTextTask",
                    "body": image_base64,
                    "phrase": False,
                    "case": True,
                    "numeric": 2,  # 字母和数字
                    "math": False,
                    "minLength": 4,
                    "maxLength": 4
                }
            }
            
            response = requests.post(create_task_url, json=task_data, timeout=30)
            result = response.json()
            
            if result.get('errorId') == 0:
                task_id = result['taskId']
                
                # 获取结果
                get_result_url = "https://api.anti-captcha.com/getTaskResult"
                for _ in range(30):
                    time.sleep(2)
                    result_data = {
                        "clientKey": self.anticaptcha_api_key,
                        "taskId": task_id
                    }
                    
                    response = requests.post(get_result_url, json=result_data, timeout=30)
                    result = response.json()
                    
                    if result.get('status') == 'ready':
                        captcha_result = result['solution']['text']
                        print(f"Anti-Captcha识别结果: {captcha_result}")
                        return captcha_result
                    elif result.get('status') != 'processing':
                        break
            
            return None
        except Exception as e:
            print(f"Anti-Captcha识别失败: {e}")
            return None
    
    def solve_with_chaojiying(self, image_data):
        """使用超级鹰识别验证码"""
        try:
            import hashlib
            
            # 计算签名
            password_md5 = hashlib.md5(self.chaojiying_pass.encode('utf-8')).hexdigest()
            sign = hashlib.md5((self.chaojiying_user + password_md5 + '9001').encode('utf-8')).hexdigest()
            
            # 提交验证码
            url = "http://upload.chaojiying.net/Upload/Processing.php"
            files = {'userfile': ('captcha.png', image_data, 'image/png')}
            data = {
                'user': self.chaojiying_user,
                'pass2': password_md5,
                'softid': self.chaojiying_soft_id,
                'codetype': '9001',  # 4位字母数字
                'sign': sign
            }
            
            response = requests.post(url, files=files, data=data, timeout=30)
            result = response.json()
            
            if result.get('err_no') == 0:
                captcha_result = result['pic_str']
                print(f"超级鹰识别结果: {captcha_result}")
                return captcha_result
            
            return None
        except Exception as e:
            print(f"超级鹰识别失败: {e}")
            return None
    
    def solve_with_ddddocr(self, image_data):
        """使用ddddocr本地识别（免费但在打包后可能有问题）"""
        try:
            log_info("开始ddddocr验证码识别", image_size=len(image_data))

            # 优先使用自定义OCR解决方案
            print("🚀 尝试自定义OCR解决方案...")
            log_info("尝试自定义OCR解决方案")
            try:
                from custom_ocr_solver import ultimate_captcha_solver
                log_info("自定义OCR模块导入成功")
                result = ultimate_captcha_solver(image_data)
                if result:
                    log_info("自定义OCR识别成功", result=result)
                    return result
                else:
                    log_error("自定义OCR识别失败，返回空结果")
            except Exception as e:
                log_exception("自定义OCR异常", error=str(e))
                print(f"⚠️ 自定义OCR失败: {e}")

            # 如果自定义OCR失败，尝试原有的ddddocr修复方案
            print("🔄 回退到ddddocr修复方案...")
            log_info("回退到ddddocr修复方案")

            # 强制应用修复方案
            log_info("开始强制应用ddddocr修复")
            self._force_apply_ddddocr_fix()

            log_info("尝试导入ddddocr模块")
            import ddddocr
            log_info("ddddocr模块导入成功")
            print("🔍 尝试使用ddddocr识别验证码...")

            try:
                # 首先尝试应用终极修复方案
                log_info("尝试应用终极修复方案")
                if not self._apply_ultimate_fix_if_needed():
                    log_error("终极修复方案应用失败")
                    print("⚠️ 终极修复方案应用失败，尝试标准方法...")
                else:
                    log_info("终极修复方案应用成功")

                # 尝试修复打包后的模型路径问题
                log_info("尝试创建ddddocr实例")
                ocr = self._create_ddddocr_with_fallback()
                if not ocr:
                    log_error("无法创建ddddocr实例")
                    print("❌ 无法创建ddddocr实例")
                    return None
                else:
                    log_info("ddddocr实例创建成功")

                log_info("开始ddddocr识别", image_size=len(image_data))
                result = ocr.classification(image_data)
                log_info("ddddocr识别完成", raw_result=result, result_type=type(result).__name__)

                # 验证结果
                if result and len(str(result).strip()) >= 3:
                    final_result = str(result).strip()
                    log_info("ddddocr识别成功", result=final_result, length=len(final_result))
                    print(f"✅ ddddocr识别成功: {final_result}")
                    return final_result
                else:
                    log_error("ddddocr识别结果异常", result=result, length=len(str(result).strip()) if result else 0)
                    print(f"⚠️ ddddocr识别结果异常: {result}")
                    return None

            except Exception as e:
                error_msg = str(e)
                log_exception("ddddocr识别异常", error=error_msg)
                print(f"❌ ddddocr识别失败: {error_msg}")

                # 检测常见的打包问题
                if any(keyword in error_msg.lower() for keyword in ['data list is empty', 'empty', 'model', 'onnx']):
                    log_error("检测到ddddocr打包兼容性问题", error_keywords=['data list is empty', 'empty', 'model', 'onnx'])
                    print("🔍 检测到ddddocr打包兼容性问题")
                    print("💡 这是PyInstaller打包ddddocr的已知问题")
                    print("🚀 尝试使用终极修复方案...")

                    # 尝试终极修复方案
                    ultimate_result = self._try_ultimate_fix_method(image_data)
                    if ultimate_result:
                        return ultimate_result

                    # 尝试备用方案
                    backup_result = self._try_ddddocr_backup_method(image_data)
                    if backup_result:
                        return backup_result

                    # 保存验证码图片用于调试
                    try:
                        with open('captcha_ddddocr_failed.png', 'wb') as f:
                            f.write(image_data)
                        print("📁 验证码图片已保存: captcha_ddddocr_failed.png")
                    except:
                        pass

                return None

        except ImportError:
            print("⚠️ ddddocr未安装，跳过本地识别")
            return None
        except Exception as e:
            print(f"❌ ddddocr模块加载失败: {e}")
            return None

    def _create_ddddocr_with_fallback(self):
        """创建ddddocr实例，包含打包后的修复方案"""
        import ddddocr
        import os
        import sys

        try:
            # 方法1: 标准创建
            ocr = ddddocr.DdddOcr(show_ad=False)
            return ocr
        except Exception as e1:
            print(f"标准方法失败: {e1}")

            try:
                # 方法2: 尝试指定模型路径
                if getattr(sys, 'frozen', False):
                    # 打包后的环境
                    base_path = sys._MEIPASS
                    model_path = os.path.join(base_path, 'ddddocr')
                    if os.path.exists(model_path):
                        print(f"找到打包后的模型路径: {model_path}")
                        # 尝试设置环境变量
                        os.environ['DDDDOCR_MODEL_PATH'] = model_path

                ocr = ddddocr.DdddOcr(show_ad=False)
                return ocr
            except Exception as e2:
                print(f"路径修复方法失败: {e2}")

                try:
                    # 方法3: 使用beta参数
                    ocr = ddddocr.DdddOcr(show_ad=False, beta=True)
                    return ocr
                except Exception as e3:
                    print(f"beta方法失败: {e3}")
                    return None

    def _try_ddddocr_backup_method(self, image_data):
        """ddddocr备用识别方法"""
        try:
            import ddddocr
            import tempfile
            import os

            # 尝试使用不同的初始化参数
            init_methods = [
                {'show_ad': False, 'beta': True},
                {'show_ad': False, 'old': True},
                {'show_ad': False, 'det': False},
            ]

            for method_params in init_methods:
                try:
                    print(f"尝试参数: {method_params}")
                    ocr = ddddocr.DdddOcr(**method_params)
                    result = ocr.classification(image_data)
                    if result and len(str(result).strip()) >= 3:
                        print(f"✅ 备用方法成功: {result}")
                        return str(result).strip()
                except Exception as e:
                    print(f"备用方法失败: {e}")
                    continue

            return None
        except Exception as e:
            print(f"所有备用方法都失败: {e}")
            return None

    def _apply_ultimate_fix_if_needed(self):
        """如果需要，应用终极修复方案"""
        try:
            # 检查是否为打包环境
            if not getattr(sys, 'frozen', False):
                return True  # 开发环境不需要修复

            # 尝试导入并应用终极修复
            try:
                from ddddocr_ultimate_fix import apply_ultimate_ddddocr_fix
                return apply_ultimate_ddddocr_fix()
            except ImportError:
                print("⚠️ 未找到终极修复模块")
                return False
        except Exception as e:
            print(f"⚠️ 应用终极修复时出错: {e}")
            return False

    def _try_ultimate_fix_method(self, image_data):
        """尝试使用终极修复方案识别验证码"""
        try:
            print("🔧 尝试终极修复方案...")

            # 应用终极修复
            if self._apply_ultimate_fix_if_needed():
                print("✅ 终极修复应用成功，重新尝试识别...")

                import ddddocr

                # 重新尝试创建OCR实例
                try:
                    ocr = ddddocr.DdddOcr(show_ad=False)
                    result = ocr.classification(image_data)

                    if result and len(str(result).strip()) >= 3:
                        print(f"✅ 终极修复方案识别成功: {result}")
                        return str(result).strip()
                    else:
                        print(f"⚠️ 终极修复方案识别结果异常: {result}")
                        return None

                except Exception as e:
                    print(f"❌ 终极修复方案仍然失败: {e}")
                    return None
            else:
                print("❌ 终极修复方案应用失败")
                return None

        except Exception as e:
            print(f"❌ 终极修复方案出错: {e}")
            return None

    def _force_apply_ddddocr_fix(self):
        """强制应用ddddocr修复方案"""
        try:
            # 检查是否为打包环境
            if not getattr(sys, 'frozen', False):
                return True  # 开发环境不需要修复

            print("🔧 强制应用ddddocr修复...")

            # 获取基础路径
            base_path = sys._MEIPASS

            # 搜索所有.onnx模型文件
            onnx_files = {}
            model_names = ['common.onnx', 'common_det.onnx', 'common_old.onnx']

            for root, dirs, files in os.walk(base_path):
                for file in files:
                    if file in model_names:
                        full_path = os.path.join(root, file)
                        size = os.path.getsize(full_path)
                        if size > 1000:  # 确保不是空文件
                            onnx_files[file] = full_path
                            print(f"✅ 找到模型: {file} ({size} bytes)")

            if not onnx_files:
                print("❌ 未找到任何ddddocr模型文件")
                return False

            # 创建临时目录
            import tempfile
            import shutil
            temp_dir = tempfile.mkdtemp(prefix='ddddocr_force_fix_')
            print(f"📁 创建临时模型目录: {temp_dir}")

            # 复制模型文件
            for model_name, source_path in onnx_files.items():
                dest_path = os.path.join(temp_dir, model_name)
                shutil.copy2(source_path, dest_path)
                print(f"📋 复制模型: {model_name}")

            # 设置环境变量
            os.environ['DDDDOCR_MODEL_PATH'] = temp_dir
            print(f"🔧 设置模型路径: {temp_dir}")

            # 强制修补ddddocr模块
            try:
                import ddddocr

                # 如果ddddocr已经被导入，重新加载它
                import importlib
                importlib.reload(ddddocr)

                # 修补DdddOcr类
                if hasattr(ddddocr, 'DdddOcr'):
                    original_init = ddddocr.DdddOcr.__init__

                    def force_patched_init(self, show_ad=True, old=False, beta=False, det=False, ocr=True):
                        """强制修补的初始化方法"""
                        try:
                            # 临时切换到模型目录
                            old_cwd = os.getcwd()
                            old_path = os.environ.get('PATH', '')

                            try:
                                os.chdir(temp_dir)
                                os.environ['PATH'] = temp_dir + os.pathsep + old_path
                                return original_init(self, show_ad=show_ad, old=old, beta=beta, det=det, ocr=ocr)
                            finally:
                                os.chdir(old_cwd)
                                os.environ['PATH'] = old_path

                        except Exception as e:
                            if "data list is empty" in str(e).lower():
                                print("🔧 检测到模型加载问题，使用强制修复...")

                                # 尝试直接设置模型路径
                                try:
                                    # 修改ddddocr内部的模型路径
                                    if hasattr(self, '__dict__'):
                                        self.__dict__['model_path'] = temp_dir

                                    # 再次尝试初始化
                                    return original_init(self, show_ad=show_ad, old=old, beta=beta, det=det, ocr=ocr)
                                except:
                                    # 最后的尝试：使用最简单的参数
                                    return original_init(self, show_ad=False, old=False, beta=False, det=False, ocr=True)
                            else:
                                raise e

                    # 应用强制补丁
                    ddddocr.DdddOcr.__init__ = force_patched_init
                    print("✅ 强制ddddocr补丁应用成功")

                    # 测试补丁是否工作
                    try:
                        test_ocr = ddddocr.DdddOcr(show_ad=False)
                        print("✅ 强制修复测试成功")
                        return True
                    except Exception as e:
                        print(f"⚠️ 强制修复测试失败: {e}")
                        return False

            except Exception as e:
                print(f"⚠️ 强制修补ddddocr失败: {e}")
                return False

        except Exception as e:
            print(f"❌ 强制修复过程失败: {e}")
            return False
    
    def recognize_captcha(self, image_data):
        """综合识别验证码 - 优先使用在线服务避免ddddocr打包问题"""
        print("开始使用专业验证码识别服务...")

        # 保存原始图片用于调试
        try:
            with open('captcha_for_api.png', 'wb') as f:
                f.write(image_data)
        except:
            pass

        results = []

        # 1. 优先尝试在线付费服务（更稳定）
        if self.twocaptcha_api_key != "YOUR_2CAPTCHA_API_KEY":
            print("\n--- 尝试2captcha (付费，优先) ---")
            result = self.solve_with_2captcha(image_data)
            if result and len(result) == 4:
                results.append(('2captcha', result))
                print(f"✅ 2captcha识别成功: {result}")
                return result  # 直接返回，避免ddddocr问题

        if self.anticaptcha_api_key != "YOUR_ANTICAPTCHA_API_KEY":
            print("\n--- 尝试Anti-Captcha (付费，优先) ---")
            result = self.solve_with_anticaptcha(image_data)
            if result and len(result) == 4:
                results.append(('anticaptcha', result))
                print(f"✅ Anti-Captcha识别成功: {result}")
                return result  # 直接返回，避免ddddocr问题

        if self.chaojiying_user != "YOUR_CHAOJIYING_USER":
            print("\n--- 尝试超级鹰 (付费，优先) ---")
            result = self.solve_with_chaojiying(image_data)
            if result and len(result) == 4:
                results.append(('chaojiying', result))
                print(f"✅ 超级鹰识别成功: {result}")
                return result  # 直接返回，避免ddddocr问题

        # 2. 尝试强制修复后的ddddocr（优先使用免费方案）
        print("\n--- 尝试强制修复后的ddddocr (免费，已修复打包问题) ---")
        result = self.solve_with_ddddocr(image_data)
        if result and len(result) >= 3:  # 放宽验证条件
            results.append(('ddddocr', result))
            print(f"✅ ddddocr识别成功: {result}")
            return result

        # 3. 如果ddddocr也失败，提供解决方案
        print("\n❌ 所有识别服务都失败了")
        print("💡 建议解决方案:")
        print("   1. 配置在线验证码识别服务API密钥")
        print("   2. 使用cx_Freeze重新打包程序")
        print("   3. 检查网络连接")

        return ""

    def solve_captcha_interactive(self, image_data):
        """交互式验证码解决 - 当自动识别失败时使用"""
        try:
            # 保存验证码图片供用户查看
            with open('current_captcha.png', 'wb') as f:
                f.write(image_data)

            print("🔍 自动识别失败，请查看 current_captcha.png 文件")
            print("📁 验证码图片已保存为 current_captcha.png")

            # 手动输入验证码
            captcha_code = input("请手动输入验证码: ").strip()

            if captcha_code:
                print(f"✅ 手动输入验证码: {captcha_code}")
                return captcha_code
            else:
                print("❌ 未输入验证码")
                return ""

        except Exception as e:
            print(f"交互式验证码处理失败: {e}")
            return ""

def test_solver():
    """测试识别器"""
    solver = ProfessionalCaptchaSolver()
    
    # 测试当前目录下的验证码图片
    import os
    test_files = ['captcha_original.png', 'captcha_for_api.png']
    
    for filename in test_files:
        if os.path.exists(filename):
            print(f"\n=== 测试文件: {filename} ===")
            with open(filename, 'rb') as f:
                image_data = f.read()
            
            result = solver.recognize_captcha(image_data)
            print(f"最终识别结果: {result}")
            
            actual = input("请输入实际验证码进行对比: ").strip()
            if actual:
                if result.lower() == actual.lower():
                    print("✅ 识别正确!")
                else:
                    print(f"❌ 识别错误! 实际: {actual}")
            break

if __name__ == "__main__":
    test_solver()
